package utils;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Utils {

	public static String[] splitLine(String line, String delimiter) {
		if(delimiter.equals("|")){
			delimiter = "\\|";
		}
		
		if(delimiter.equals(".")){
			delimiter = "\\.";
		}
		
		String[] rtn = line.split(delimiter);
		for(int i=0;i<rtn.length;i++){
			rtn[i] = rtn[i].trim();
		}
		
		return rtn;
	}

	public static String[][] readlines(String dataFile) {
		return readAndSkipLines(dataFile, 0);
	}

	/**
	 * reads the lines from <code>dataFile</code>, ignoring the first 
	 * <code>toSkip</code> lines.
	 * 
	 * @param dataFile -- the filename of the file to read
	 * @param toSkip -- the number of lines to Skip
	 * @return -- a 2D array of Strings representing the file lines.
	 */
	public static String[][] readAndSkipLines(String dataFile, int toSkip) {
		List<String[]> lines = new ArrayList<String[]>();
		String line = null;
		
		
		try{
			FileReader fr = new FileReader(new File(dataFile));
			BufferedReader br = new BufferedReader(fr);
			
			while((line = br.readLine()) != null){
				lines.add(Utils.splitLine(line, "|"));
			}
			
		}catch(IOException e){
			e.printStackTrace();
		}
		
		for(int i=0;i<toSkip;i++){
			lines.remove(i);
		}
		
		String[][] linesArray = new String[lines.size()][];
		lines.toArray(linesArray);
		return linesArray;
	}
	
	public static List<String[]> readAndSkipLinesList(String dataFile, int toSkip) {
		List<String[]> lines = new ArrayList<String[]>();
		String line = null;
		
		
		try{
			FileReader fr = new FileReader(new File(dataFile));
			BufferedReader br = new BufferedReader(fr);
			
			while((line = br.readLine()) != null){
				lines.add(Utils.splitLine(line, "|"));
			}
			
		}catch(IOException e){
			e.printStackTrace();
		}
		
		for(int i=0;i<toSkip;i++){
			lines.remove(i);
		}
		
		return lines;
	}
	
	public static List<String[]> readLinesList(String dataFile){
		return readAndSkipLinesList(dataFile, 0);
	}

	public static  String[][] transposeArray(String[][] input) {
		int inRows = input.length;
		int inCols = input[0].length;
		
		String[][] rtn = new String[inCols][inRows];
		for(int i=0;i<inRows;i++){
			for(int j=0;j<inCols;j++){
				rtn[j][i] = input[i][j];
			}
		}
		
		return rtn;
	}
	
	public static  double[][] transposeArray(double[][] input) {
		int inRows = input.length;
		int inCols = input[0].length;
		
		double[][] rtn = new double[inCols][inRows];
		for(int i=0;i<inRows;i++){
			for(int j=0;j<inCols;j++){
				rtn[j][i] = input[i][j];
			}
		}
		
		return rtn;
	}

	public static List<String> readFile(String dataFile) {
		List<String> lines = new ArrayList<String>();
		String line = null;
		
		
		try{
			FileReader fr = new FileReader(new File(dataFile));
			BufferedReader br = new BufferedReader(fr);
			
			while((line = br.readLine()) != null){
				lines.add(line);
			}
			
		}catch(IOException e){
			e.printStackTrace();
		}
		
		return lines;
	}

	public static String[] getFileHeader(String dataFile) {
		return Utils.readlines(dataFile)[0];
	}

	public static double[] convertToDoubles(String[] input) {
		double[] output = new double[input.length];
		for(int i=0;i<output.length;i++){
			output[i] = Double.parseDouble(input[i]);
		}
		return output;
	}

	public static double[][] convertToDoubles(String[][] input) {
		int nRows = input.length;
		double[][] output = new double[nRows][];
		for(int i=0;i<nRows;i++){
			double[] temp = new double[input[i].length];
			for(int j=0;j<temp.length;j++){
				temp[j] = Double.parseDouble(input[i][j]); 
			}
			output[i] = temp;
		}
		return output;
	}

	public static double[][] dropColumn(double[][] input, int colNum) {
		if(colNum<=0 || colNum>input.length){
			return null;
		}
		
		List<double[]> colsToKeep = new ArrayList<double[]>(Arrays.asList(input));
		colsToKeep.remove(colNum - 1);
		double[][] rtn = new double[colsToKeep.size()][];
		colsToKeep.toArray(rtn);
		return rtn;
	}
	
	public static double mean(double[] vals){
		if(vals == null || vals.length == 0){
			return 0.0;
		}else{
			double sum = sum(vals);
			double n = 1.0 * vals.length;
			return sum/n;
		}
	}
	
	public static double sum(double[] vals){
		double acc = 0;
		if(vals != null){
			for(int i=0;i<vals.length;i++){
				acc += vals[i];
			}
		}
		
		return acc;
	}
	
	public static double variance(double[] vals){
		
		double mean = mean(vals);
		double[] squareDiffs = new double[vals.length];
		
		for(int i=0;i<squareDiffs.length;i++){
			double val = vals[i];
			double diff = val - mean;
			squareDiffs[i] = Math.pow(diff, 2);
		}
	
		return mean(squareDiffs);
		
	}
	
	public static double stdev(double[] vals){
		double variance = variance(vals);
		return Math.sqrt(variance);
	}
	
	public static double rootMeanSquare(double[] vals){
		double acc = 0.0;
		for(int i=0;i<vals.length;i++){
			acc += Math.pow(vals[i], 2);
		}
		
		return Math.sqrt(acc/vals.length);
	}

	/**
	 * adds the specified column to the end of the array
	 * 
	 * @param matrix the 2d double array to add a column
	 * @param b the column to add
	 * @exception IllegalArgumentException if the length of b is not equal
	 * to the number of rows in the array.
	 * @return
	 */
	public static double[][] addColumn(double[][] matrix, double[] b) {
		if(matrix.length != b.length){
			throw new IllegalArgumentException();
		}
		
		double[][] rtn = new double[b.length][matrix[0].length + 1];
		for(int i=0;i < rtn.length;i++){
			rtn[i][rtn[i].length - 1] = b[i];
			for(int j=0;j<matrix[i].length;j++){
				rtn[i][j] = matrix[i][j];
			}
		}
		return rtn;
	}

	public static double[] ero1(double[][] matrix, double scalar, int rowToScale, int rowToAdd) {
		double[] scaledRow = Utils.scaleVector(matrix[rowToScale], scalar);
		int n = scaledRow.length;
		for(int i=0;i<n;i++){
			matrix[rowToAdd][i] += scaledRow[i];
		}
		return matrix[rowToAdd];
	}

	public static double[] scaleVector(double[] vector, double scalar){
		double[] rtn = new double[vector.length];
		for(int i=0;i<rtn.length;i++){
			rtn[i] = scalar * vector[i];
		}
		
		return rtn;
	}

	public static double[][] arrayCopy2D(double[][] input) {
		int rows = input.length;
		int cols = input[0].length;
	
		double[][] copy = new double[rows][cols];
		
		for(int i = 0;i<rows;i++){
			for(int j=0;j<cols;j++){
				copy[i][j] = input[i][j];
			}
		}
		return copy;
	}

	/**
	 * swaps <code>row1</code> with <code>row2</code>
	 * 
	 * @param input
	 * @param row1
	 * @param row2
	 * @return
	 */
	public static double[][] ero2(double[][] input, int row1, int row2) {
		List<double[]> rows = new ArrayList<double[]>(Arrays.asList(input));
		rows.set(row2, input[row1]);
		rows.set(row1, input[row2]);
		double[][] rtn = new double[rows.size()][];
		return rows.toArray(rtn);
	}

	public static double[] getDoubleValues(List<Double> input) {
		Double[] rtn = new Double[input.size()];
		input.toArray(rtn);
		double[] rtnd = new double[rtn.length];
		for(int i=0;i<rtnd.length;i++){
			rtnd[i] = rtn[i].doubleValue();
		}
		
		return rtnd;
	}

	
	
	
}
